"use strict";

const { Client: ScpClient } = require("node-scp");
const { Client: SshClient } = require("ssh2");
const spawn = require("child_process").spawn;
const cliPkgJson = require("../package.json"); // 脚手架的脚本
const simpleGit = require("simple-git");
const commander = require("commander");
const inquirer = require("inquirer");
const npmlog = require("npmlog");
const axios = require("axios");
const path = require("path");
const util = require("util");
const fs = require("fs");
const sshConn = new SshClient();
const cwd = process.cwd();
const CONFIG_FILENAME = "zmo-cli.config";

const preUpgradePkgVersion = (type) => {
  const pkgJson = require(path.join(cwd, "package.json"));
  const versionArr = pkgJson.version.split(".");
  switch (type) {
    case "x":
      versionArr[0]++;
      versionArr[1] = 0;
      versionArr[2] = 0;
      break;
    case "y":
      versionArr[1]++;
      versionArr[2] = 0;
      break;
    case "z":
      versionArr[2]++;
      break;
  }

  pkgJson.version = versionArr.join(".");
  return pkgJson;
};

const upgradePkgVersion = (pkgJson) => {
  try {
    fs.writeFileSync("./package.json", JSON.stringify(pkgJson, null, "\t"));
  } catch (e) {
    npmlog.error("更新版本号失败", e.message);
  }
};

const checkGitConfig = async () => {
  const gitUserName = (await simpleGit().getConfig("user.name", "global"))
    .value;
  if (!gitUserName) {
    npmlog.error(
      "git配置缺失",
      "请先通过 git config --global user.name '你的用户名' 配置git用户名"
    );
    process.exit(1);
  }
  const gitUserEmail = (await simpleGit().getConfig("user.email", "global"))
    .value;
  if (!gitUserEmail) {
    npmlog.error(
      "git配置缺失",
      "请先通过 git config --global user.email '你的邮箱' 配置git用户名"
    );
    process.exit(1);
  }
};

/**
 * checkIsFrontEndProjectDirectory 检查是否为前端项目工作目录
 */
const checkIsFrontEndProjectDirectory = async () => {
  const cwdPkgPath = path.join(cwd, "package.json");
  try {
    await util.promisify(fs.stat)(cwdPkgPath);
  } catch {
    npmlog.error(
      "工作路径不正确",
      "看上去当前目录不是一个前端项目目录, 请到项目目录执行该命令"
    );
    process.exit(1);
  }
};

const checkIsConfigFileExist = async () => {
  const cwdConfPath = path.join(cwd, `${CONFIG_FILENAME}.js`);
  try {
    await util.promisify(fs.stat)(cwdConfPath);
  } catch {
    npmlog.warn(
      "配置文件不存在",
      `${CONFIG_FILENAME}配置文件不存在, 请使用zmo-cli ge初始化配置文件`
    );
    process.exit(1);
  }
};

const removeOldProject = (service) => {
  const { host, username, credentialsLoc } = service;
  return new Promise((resolve, reject) => {
    sshConn
      .on("ready", () => {
        // conn.exec("rm -rf /home/ec2-user/docker/docker_nginx/html/*", (err, stream) => {
        sshConn.exec("pwd", (err, stream) => {
          if (err) throw err;
          stream
            .on("close", (code, signal) => {
              if (code === 0) {
                // 执行成功了
                resolve();
              } else {
                // 执行失败了
                reject();
              }
              sshConn.end();
            })
            .on("data", (data) => {
              npmlog.notice("清空远程服务器目录ing");
            })
            .stderr.on("data", (data) => {
              npmlog.error("清空远程服务器目录出错", data);
            });
        });
      })
      .connect({
        host,
        port: 22,
        username,
        privateKey: fs.readFileSync(path.join(cwd, credentialsLoc)),
      });
    // TODO 处理错误连接
  });
};

const copyProject2Service = (service, outputDir) => {
  const { host, username, credentialsLoc } = service;
  return new Promise((resolve, reject) => {
    ScpClient({
      host,
      port: 22,
      username,
      privateKey: fs.readFileSync(path.join(cwd, credentialsLoc)),
    })
      .then((client) => {
        client
          .uploadDir(
            path.join(cwd, outputDir),
            service.directory
            // options:
          )
          .then(() => {
            npmlog.notice("部署成功", `ip: ${service.host}`);
            resolve();
            client.close(); // remember to close connection after you finish
          })
          .catch((error) => {
            npmlog.error("部署失败", `ip: ${service.host}`, error);
            reject();
          });
      })
      .catch((e) => {
        npmlog.error("部署失败", `ip: ${service.host}`, e);
        reject();
      });
  });
};

/**
 * 调用接口刷新dns
 */
const refreshCdn = (address, cdnRefreshApi) => {
  return axios.get(cdnRefreshApi).then((res) => {
    if (res.status !== 200) {
      npmlog.error("DNS刷新失败", `ip: ${address}`);
    } else {
      npmlog.notice("DNS刷新成功", `ip: ${address}`);
    }
  });
};

/**
 * sendFeishuNotice
 */
const sendFeishuNotice = async (
  deployInfo,
  serviceName,
  address,
  isProduction,
  feishuNoticeApi,
  currentVersion
) => {
  const title = `【${serviceName}】新版本发布`;
  const titleBackgroundColor = isProduction ? "purple" : "grey";

  return axios
    .post(feishuNoticeApi, {
      msg_type: "interactive",
      card: {
        config: {
          wide_screen_mode: true,
        },
        elements: [
          {
            tag: "div",
            text: {
              content: `**🔖 内容摘要：**\n${deployInfo}\n\n🧩 **当前版本：** \n${currentVersion}\n\n **👨‍💻 开发者：**\n${
                (await simpleGit().getConfig("user.name", "global")).value
              }\n`,
              tag: "lark_md",
            },
          },
          {
            actions: [
              {
                tag: "button",
                text: {
                  content: "立即查看",
                  tag: "plain_text",
                },
                type: "primary",
                url: address,
              },
            ],
            tag: "action",
          },
        ],
        header: {
          template: titleBackgroundColor,
          title: {
            content: title,
            tag: "plain_text",
          },
        },
      },
    })
    .then((res) => {
      if (res.data.code === 19002) {
        npmlog.error("发送飞书通知失败", `ip: ${address}`, res.data.msg);
        return;
      }
      npmlog.notice("发送飞书通知成功", `ip: ${address}`);
    })
    .catch((error) => {
      npmlog.error("发送飞书通知失败", error);
    });
};

const gitCommit = (commitInfo) => {
  simpleGit().add("./*").commit(`ci: ${commitInfo}`);
};

const commands = () => {
  commander
    .version(cliPkgJson.version)
    .command("generate")
    .alias("ge")
    .description("generate a config file for publish")
    .action(async () => {
      await checkIsFrontEndProjectDirectory();
      const cwdConfPath = path.join(cwd, `${CONFIG_FILENAME}.js`);
      try {
        await util.promisify(fs.stat)(cwdConfPath);
        npmlog.warn(`${CONFIG_FILENAME}文件已经存在, 请删除文件后再试`);
        process.exit(1);
      } catch {
        // generate a config file
        const configFileExample = fs.readFileSync(
          path.join(__dirname, `../models/${CONFIG_FILENAME}.js`)
        );
        fs.writeFileSync(cwdConfPath, configFileExample);
      }
    });

  commander
    .command("publish")
    .alias("pb")
    .description("public current project")
    .action(async () => {
      // const pkgJson = require(path.join(cwd, "package.json"));
      // const cwdConfPath = path.join(cwd, `${CONFIG_FILENAME}.js`);
      // const aaaaa = ["abroad"];
      // await new Promise(async (resolve) => {
      //   // 检查配置文件
      //   try {
      //     const config = require(cwdConfPath);
      //     for (const env of Object.keys(config.deploy)) {
      //       // 分环境构建
      //       if (!aaaaa.includes(env)) continue;
      //       if (
      //         config.deploy[env].services &&
      //         config.deploy[env].services.length
      //       ) {
      //         // 多台服务器部署
      //         for (const service of config.deploy[env].services) {
      //           await removeOldProject(service);
      //           await copyProject2Service(service, config.outputDir);
      //           if (service.cdnRefreshApi)
      //             await refreshCdn(service.address, service.cdnRefreshApi);
      //           if (service.feishuNoticeApi)
      //             await sendFeishuNotice(
      //               "测试发布海外服务器",
      //               config.deploy[env].label,
      //               service.address,
      //               service.isProduction,
      //               service.feishuNoticeApi,
      //               pkgJson.version
      //             );
      //         }
      //       }
      //     }
      //     resolve();
      //   } catch (err) {
      //     npmlog.error(`部署出错了: ${err}`);
      //     process.exit(1);
      //   }
      // });

      // return;

      await checkIsFrontEndProjectDirectory();
      await checkGitConfig();
      await checkIsConfigFileExist();

      const config = require(path.join(cwd, `${CONFIG_FILENAME}.js`));
      const deployEnvsOptions = Object.keys(config.deploy).map((key) => ({
        value: key,
        name: config.deploy[key].label,
      }));

      const {
        description,
        deployEnvs,
        upgradePkgVersionType,
        isNeedFeishuNotice,
      } = await inquirer.prompt([
        {
          type: "input",
          name: "description",
          message: "请输入部署摘要:",
          validate: function (value) {
            const done = this.async();
            if (!value.trim()) {
              done("部署摘要是必填项");
            } else {
              done(null, true);
            }
          },
        },
        {
          type: "checkbox",
          name: "deployEnvs",
          message: "请选择需要发布的环境: ",
          choices: [...deployEnvsOptions],
          validate: function (value) {
            const done = this.async();
            if (!value.length) {
              done("至少选择一个部署环境!");
            } else {
              done(null, true);
            }
          },
        },
        {
          type: "list",
          name: "upgradePkgVersionType",
          message: "请选择需要更新的版本号位: ",
          choices: [
            { name: "z", value: "z" },
            { name: "y", value: "y" },
            { name: "x", value: "x" },
          ],
        },
        {
          type: "confirm",
          name: "isNeedFeishuNotice",
          message: "需要发送飞书通知吗?",
        },
      ]);

      // 构建开始时间戳
      const startBuildTime = Date.now();
      const cwdConfPath = path.join(cwd, `${CONFIG_FILENAME}.js`);

      const pkgJson = preUpgradePkgVersion(upgradePkgVersionType);
      await new Promise(async (resolve) => {
        // 检查配置文件
        try {
          const config = require(cwdConfPath);
          for (const env of Object.keys(config.deploy)) {
            // 分环境构建
            if (!deployEnvs.includes(env)) continue;
            await new Promise((resolveBuild) => {
              const builder = spawn(
                process.platform === "win32" ? "npm.cmd" : "npm",
                [...config.deploy[env].command],
                {
                  stdio: "pipe",
                  cwd: process.cwd(),
                }
              );

              builder.stdout.pipe(process.stdout);
              builder.stderr.pipe(process.stderr);

              builder.on("close", (code) => {
                npmlog.notice(
                  "构建完成",
                  `当前环境【${config.deploy[env].label}】, finish code: ${code}`
                );
                if (code === 0) {
                  resolveBuild(code);
                } else {
                  npmlog.error(
                    "构建失败",
                    `当前环境【${config.deploy[env].label}】, finish code: ${code}`
                  );
                  process.exit(1);
                }
              });
            });

            if (
              config.deploy[env].services &&
              config.deploy[env].services.length
            ) {
              // 多台服务器部署
              for (const service of config.deploy[env].services) {
                await removeOldProject(service);
                await copyProject2Service(service, config.outputDir);
                if (service.cdnRefreshApi)
                  await refreshCdn(service.address, service.cdnRefreshApi);
              }

              if (isNeedFeishuNotice && config.deploy[env].feishuNoticeApi)
                await sendFeishuNotice(
                  description,
                  config.deploy[env].label,
                  config.deploy[env].address,
                  config.deploy[env].isProduction,
                  config.deploy[env].feishuNoticeApi,
                  pkgJson.version
                );
            }
          }
          resolve();
        } catch (err) {
          npmlog.error(`部署出错了: ${err}`);
          process.exit(1);
        }
      });

      // 全部构建完成
      // 升级版本号
      upgradePkgVersion(pkgJson);
      // 提交commit
      gitCommit(description);

      // 构建结束时间戳
      const finishBuildTime = Date.now();
      const buildDuration = finishBuildTime - startBuildTime;
      npmlog.timing(
        "发布完成",
        `本次发布总共耗时${Math.floor(buildDuration / 1000)}秒`
      );
    });

  commander.parse(process.argv); // 解析命令行参数
};

module.exports = commands;
